/**
 * Utility for measuring performance
 */

/**
 * Performance measurement result
 */
export interface PerformanceResult {
  operation: string;
  duration: number;
  memoryUsage?: number;
  imageCount?: number;
  totalPixels?: number;
  averagePixelsPerSecond?: number;
  details?: Record<string, any>;
}

/**
 * Measure the performance of a function
 * @param operation Operation name
 * @param fn Function to measure
 * @param details Additional details to include in the result
 * @returns Promise with performance result
 */
export async function measurePerformance<T>(
  operation: string,
  fn: () => Promise<T>,
  details?: Record<string, any>
): Promise<{ result: T; performance: PerformanceResult }> {
  // Get initial memory usage if available
  const initialMemory = getMemoryUsage();
  
  // Start timer
  const startTime = performance.now();
  
  // Execute function
  const result = await fn();
  
  // End timer
  const endTime = performance.now();
  
  // Calculate duration
  const duration = endTime - startTime;
  
  // Get final memory usage if available
  const finalMemory = getMemoryUsage();
  
  // Calculate memory usage
  const memoryUsage = finalMemory && initialMemory ? finalMemory - initialMemory : undefined;
  
  // Create performance result
  const performanceResult: PerformanceResult = {
    operation,
    duration,
    memoryUsage,
    details
  };
  
  // Calculate pixels per second if image count and total pixels are provided
  if (details?.imageCount && details?.totalPixels) {
    performanceResult.imageCount = details.imageCount;
    performanceResult.totalPixels = details.totalPixels;
    performanceResult.averagePixelsPerSecond = details.totalPixels / (duration / 1000);
  }
  
  return { result, performance: performanceResult };
}

/**
 * Get current memory usage in bytes
 * @returns Memory usage in bytes or undefined if not available
 */
function getMemoryUsage(): number | undefined {
  if ('performance' in window && 'memory' in performance) {
    const memory = (performance as any).memory;
    if (memory && memory.usedJSHeapSize) {
      return memory.usedJSHeapSize;
    }
  }
  return undefined;
}

/**
 * Format performance result as a string
 * @param result Performance result
 * @returns Formatted string
 */
export function formatPerformanceResult(result: PerformanceResult): string {
  let output = `Operation: ${result.operation}\n`;
  output += `Duration: ${result.duration.toFixed(2)} ms\n`;
  
  if (result.memoryUsage !== undefined) {
    output += `Memory Usage: ${formatBytes(result.memoryUsage)}\n`;
  }
  
  if (result.imageCount !== undefined) {
    output += `Image Count: ${result.imageCount}\n`;
  }
  
  if (result.totalPixels !== undefined) {
    output += `Total Pixels: ${result.totalPixels.toLocaleString()}\n`;
  }
  
  if (result.averagePixelsPerSecond !== undefined) {
    output += `Processing Speed: ${formatNumber(result.averagePixelsPerSecond)} pixels/second\n`;
  }
  
  if (result.details) {
    output += 'Details:\n';
    for (const [key, value] of Object.entries(result.details)) {
      if (key !== 'imageCount' && key !== 'totalPixels') {
        output += `  ${key}: ${value}\n`;
      }
    }
  }
  
  return output;
}

/**
 * Format bytes as a human-readable string
 * @param bytes Bytes
 * @returns Formatted string
 */
function formatBytes(bytes: number): string {
  if (bytes === 0) return '0 Bytes';
  
  const k = 1024;
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}

/**
 * Format number with commas
 * @param num Number
 * @returns Formatted string
 */
function formatNumber(num: number): string {
  return num.toLocaleString(undefined, { maximumFractionDigits: 0 });
}

/**
 * Log performance result to console
 * @param result Performance result
 */
export function logPerformanceResult(result: PerformanceResult): void {
  console.log(formatPerformanceResult(result));
}

/**
 * Run a performance test multiple times and average the results
 * @param operation Operation name
 * @param fn Function to measure
 * @param iterations Number of iterations
 * @param details Additional details to include in the result
 * @returns Promise with average performance result
 */
export async function runPerformanceTest<T>(
  operation: string,
  fn: () => Promise<T>,
  iterations: number = 3,
  details?: Record<string, any>
): Promise<{ result: T; performance: PerformanceResult }> {
  let totalDuration = 0;
  let totalMemoryUsage = 0;
  let memoryUsageCount = 0;
  let lastResult: T | undefined;
  
  for (let i = 0; i < iterations; i++) {
    const { result, performance } = await measurePerformance(operation, fn, details);
    totalDuration += performance.duration;
    
    if (performance.memoryUsage !== undefined) {
      totalMemoryUsage += performance.memoryUsage;
      memoryUsageCount++;
    }
    
    lastResult = result;
    
    // Wait a bit between iterations to allow for garbage collection
    await new Promise(resolve => setTimeout(resolve, 100));
  }
  
  const averageDuration = totalDuration / iterations;
  const averageMemoryUsage = memoryUsageCount > 0 ? totalMemoryUsage / memoryUsageCount : undefined;
  
  const averagePerformance: PerformanceResult = {
    operation,
    duration: averageDuration,
    memoryUsage: averageMemoryUsage,
    details
  };
  
  // Calculate pixels per second if image count and total pixels are provided
  if (details?.imageCount && details?.totalPixels) {
    averagePerformance.imageCount = details.imageCount;
    averagePerformance.totalPixels = details.totalPixels;
    averagePerformance.averagePixelsPerSecond = details.totalPixels / (averageDuration / 1000);
  }
  
  return { result: lastResult as T, performance: averagePerformance };
}